home *** CD-ROM | disk | FTP | other *** search
/ Almathera Ten Pack 2: CDPD 1 / Almathera Ten on Ten - Disc 2: CDPD 1.iso / pd / 051-075 / 074 / less / line.c < prev    next >
C/C++ Source or Header  |  1995-03-13  |  8KB  |  352 lines

  1. /*
  2.  * Routines to manipulate the "line buffer".
  3.  * The line buffer holds a line of output as it is being built
  4.  * in preparation for output to the screen.
  5.  * We keep track of the PRINTABLE length of the line as it is being built.
  6.  */
  7.  
  8. #include "less.h"
  9.  
  10. static char linebuf[1024];      /* Buffer which holds the current output line */
  11. static char *curr;              /* Pointer into linebuf */
  12. static int column;              /* Printable length, accounting for
  13.                    backspaces, etc. */
  14. /*
  15.  * A ridiculously complex state machine takes care of backspaces 
  16.  * when in BS_UNDERLINE mode.  The complexity arises from the attempt
  17.  * to deal with all cases, especially involving long lines with underlining.
  18.  * There are still some cases which will break it.
  19.  *
  20.  * There are four states:
  21.  *      UL_NORMAL is the normal state (not in underline mode).
  22.  *      UL_YES means we are in underline mode.  We expect to get
  23.  *              either a sequence like "_\bX" or "X\b_" to continue
  24.  *              underline mode, or just some ordinary characters
  25.  *              (no backspaces) to end underline mode.
  26.  *      UL_X means we are one character after UL_YES
  27.  *              (we have gotten the '_' in "_\bX" or the 'X' in "X\b_").
  28.  *      UL_XB means we are one character after UL_X 
  29.  *              (we have gotten the backspace in "_\bX" or "X\b_";
  30.  *              we expect one more ordinary character, 
  31.  *              which will put us back in state UL_YES).
  32.  */
  33. static int ul_state;            /* Currently in underline mode? */
  34. #define UL_NORMAL       0       /* Not in underline mode */
  35. #define UL_YES          1       /* In underline, need next char */
  36. #define UL_X            2       /* In underline, got char, need \b */
  37. #define UL_XB           3       /* In underline, got char & \b, need one more */
  38.  
  39. public char *line;              /* Pointer to the current line.
  40.                    Usually points to linebuf. */
  41.  
  42. extern int bs_mode;
  43. extern int tabstop;
  44. extern int ul_width, ue_width;
  45. extern int sc_width, sc_height;
  46.  
  47. /*
  48.  * Rewind the line buffer.
  49.  */
  50.     public void
  51. prewind()
  52. {
  53.     line = curr = linebuf;
  54.     ul_state = UL_NORMAL;
  55.     column = 0;
  56. }
  57.  
  58. /*
  59.  * Append a character to the line buffer.
  60.  * Expand tabs into spaces, handle underlining.
  61.  * Returns 0 if ok, 1 if couldn't fit in buffer.
  62.  */
  63.  
  64. #define NEW_COLUMN(newcol)      if ((newcol) + ((ul_state)?ue_width:0) > sc_width) \
  65.                     return (1); else column = (newcol)
  66.  
  67.     public int
  68. pappend(c)
  69.     int c;
  70. {
  71.     if (c == '\0')
  72.     {
  73.         /*
  74.          * Terminate underline mode, if necessary.
  75.          * Append a '\0' to the end of the line.
  76.          */
  77.         switch (ul_state)
  78.         {
  79.         case UL_X:
  80.             curr[0] = curr[-1];
  81.             curr[-1] = UE_CHAR;
  82.             curr++;
  83.             break;
  84.         case UL_XB:
  85.         case UL_YES:
  86.             *curr++ = UE_CHAR;
  87.             break;
  88.         }
  89.         ul_state = UL_NORMAL;
  90.         *curr = '\0';
  91.         return (0);
  92.     }
  93.  
  94.     if (curr > linebuf + sizeof(linebuf) - 12)
  95.         /*
  96.          * Almost out of room in the line buffer.
  97.          * Don't take any chances.
  98.          * {{ Linebuf is supposed to be big enough that this
  99.          *    will never happen, but may need to be made 
  100.          *    bigger for wide screens or lots of backspaces. }}
  101.          */
  102.         return (1);
  103.  
  104.     if (bs_mode == BS_UNDERLINE)
  105.     {
  106.         /*
  107.          * Advance the state machine.
  108.          */
  109.         switch (ul_state)
  110.         {
  111.         case UL_NORMAL:
  112.             if (curr <= linebuf + 1 || curr[-1] != '\b')
  113.                 break;
  114.             if (c != '_' && curr[-2] != '_')
  115.             {
  116.                 curr -= 2;
  117.                 break;
  118.             }
  119.  
  120.             /*
  121.              * We have either "_\bX" or "X\b_" (including
  122.              * the current char).  Switch into underline mode.
  123.              */
  124.             if (column + ul_width + ue_width + 1 >= sc_width)
  125.                 /*
  126.                  * Not enough room left on the screen to 
  127.                  * enter and exit underline mode.
  128.                  */
  129.                 return (1);
  130.  
  131.             if (ul_width > 0 && 
  132.                 curr > linebuf + 2 && curr[-3] == ' ')
  133.             {
  134.                 /*
  135.                  * Special case for magic cookie terminals:
  136.                  * if the previous char was a space, replace 
  137.                  * it with the "enter underline" sequence.
  138.                  */
  139.                 curr[-3] = UL_CHAR;
  140.                 column += ul_width-1;
  141.             } else
  142.             {
  143.                 curr[-1] = curr[-2];
  144.                 curr[-2] = UL_CHAR;
  145.                 column += ul_width;
  146.                 curr++;
  147.             }
  148.             /* Fall thru */
  149.         case UL_XB:
  150.             /*
  151.              * Termination of a sequnce "_\bX" or "X\b_".
  152.              */
  153.             if (c == '_')
  154.                 c = curr[-2];
  155.             curr -= 2;
  156.             ul_state = UL_YES;
  157.             break;
  158.         case UL_YES:
  159.             if (column + ue_width + 1 >= sc_width)
  160.                 /*
  161.                  * We have just barely enough room to 
  162.                  * exit underline mode.  
  163.                  */
  164.                 return (1);
  165.             ul_state = UL_X;
  166.             break;
  167.         case UL_X:
  168.             if (c == '\b')
  169.                 ul_state = UL_XB;
  170.             else
  171.             {
  172.                 /*
  173.                  * Exit underline mode.
  174.                  * We have to shuffle the chars a bit
  175.                  * to make this work.
  176.                  */
  177.                 curr[0] = curr[-1];
  178.                 curr[-1] = UE_CHAR;
  179.                 column += ue_width;
  180.                 if (ul_width > 0 && curr[0] == ' ')
  181.                     /*
  182.                      * Another special case for magic
  183.                      * cookie terminals: if the next
  184.                      * char is a space, replace it
  185.                      * with the "exit underline" sequence.
  186.                      */
  187.                     column--;
  188.                 else
  189.                     curr++;
  190.                 ul_state = UL_NORMAL;
  191.             } 
  192.             break;
  193.         }
  194.     }
  195.     
  196.     if (c == '\t') 
  197.     {
  198.         /*
  199.          * Expand a tab into spaces.
  200.          */
  201.         do
  202.         {
  203.             NEW_COLUMN(column+1);
  204.         } while ((column % tabstop) != 0);
  205.         *curr++ = '\t';
  206.         return (0);
  207.     }
  208.  
  209.     if (c == '\b')
  210.     {
  211.         if (bs_mode == BS_CONTROL)
  212.         {
  213.             /*
  214.              * Treat backspace as a control char: output "^H".
  215.              */
  216.             NEW_COLUMN(column+2);
  217.             *curr++ = ('H' | 0200);
  218.         } else
  219.         {
  220.             /*
  221.              * Output a real backspace.
  222.              */
  223.             column--;
  224.             *curr++ = '\b';
  225.         }
  226.         return (0);
  227.     } 
  228.  
  229.     if (control_char(c))
  230.     {
  231.         /*
  232.          * Put a "^X" into the buffer.
  233.          * The 0200 bit is used to tell put_line() to prefix
  234.          * the char with a ^.  We don't actually put the ^
  235.          * in the buffer because we sometimes need to move
  236.          * chars around, and such movement might separate 
  237.          * the ^ from its following character.
  238.          */
  239.         NEW_COLUMN(column+2);
  240.         *curr++ = (carat_char(c) | 0200);
  241.         return (0);
  242.     }
  243.  
  244.     /*
  245.      * Ordinary character.  Just put it in the buffer.
  246.      */
  247.     NEW_COLUMN(column+1);
  248.     *curr++ = c;
  249.     return (0);
  250. }
  251.  
  252. /*
  253.  * Analogous to forw_line(), but deals with "raw lines":
  254.  * lines which are not split for screen width.
  255.  * {{ This is supposed to be more efficient than forw_line(). }}
  256.  */
  257.     public POSITION
  258. forw_raw_line(curr_pos)
  259.     POSITION curr_pos;
  260. {
  261.     register char *p;
  262.     register int c;
  263.     POSITION new_pos;
  264.  
  265.     if (curr_pos == NULL_POSITION || ch_seek(curr_pos) ||
  266.         (c = ch_forw_get()) == EOF)
  267.         return (NULL_POSITION);
  268.  
  269.     p = linebuf;
  270.  
  271.     for (;;)
  272.     {
  273.         if (c == '\n' || c == EOF)
  274.         {
  275.             new_pos = ch_tell();
  276.             break;
  277.         }
  278.         if (p >= &linebuf[sizeof(linebuf)-1])
  279.         {
  280.             /*
  281.              * Overflowed the input buffer.
  282.              * Pretend the line ended here.
  283.              * {{ The line buffer is supposed to be big
  284.              *    enough that this never happens. }}
  285.              */
  286.             new_pos = ch_tell() - 1;
  287.             break;
  288.         }
  289.         *p++ = c;
  290.         c = ch_forw_get();
  291.     }
  292.     *p = '\0';
  293.     line = linebuf;
  294.     return (new_pos);
  295. }
  296.  
  297. /*
  298.  * Analogous to back_line(), but deals with "raw lines".
  299.  * {{ This is supposed to be more efficient than back_line(). }}
  300.  */
  301.     public POSITION
  302. back_raw_line(curr_pos)
  303.     POSITION curr_pos;
  304. {
  305.     register char *p;
  306.     register int c;
  307.     POSITION new_pos;
  308.  
  309.     if (curr_pos == NULL_POSITION || curr_pos <= (POSITION)0 ||
  310.         ch_seek(curr_pos-1))
  311.         return (NULL_POSITION);
  312.  
  313.     p = &linebuf[sizeof(linebuf)];
  314.     *--p = '\0';
  315.  
  316.     for (;;)
  317.     {
  318.         c = ch_back_get();
  319.         if (c == '\n')
  320.         {
  321.             /*
  322.              * This is the newline ending the previous line.
  323.              * We have hit the beginning of the line.
  324.              */
  325.             new_pos = ch_tell() + 1;
  326.             break;
  327.         }
  328.         if (c == EOF)
  329.         {
  330.             /*
  331.              * We have hit the beginning of the file.
  332.              * This must be the first line in the file.
  333.              * This must, of course, be the beginning of the line.
  334.              */
  335.             new_pos = (POSITION)0;
  336.             break;
  337.         }
  338.         if (p <= linebuf)
  339.         {
  340.             /*
  341.              * Overflowed the input buffer.
  342.              * Pretend the line ended here.
  343.              */
  344.             new_pos = ch_tell() + 1;
  345.             break;
  346.         }
  347.         *--p = c;
  348.     }
  349.     line = p;
  350.     return (new_pos);
  351. }
  352.